package service

import (
	"census/common/records"
	"census/common/utils"
	"census/context"
	"census/model"
	ctx "context"
	"encoding/json"
	"fmt"
	"github.com/golib/helper"
	"github.com/golib/logger"
	"github.com/golib/mysql"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"
)

type HttpService struct {
	CommonService
	port   string
	sid    string
	ch     chan *context.AppRequest
	server *http.Server
}

func NewHttpService(sid, port string) *HttpService {
	return &HttpService{
		sid:  sid,
		port: port,
		ch:   make(chan *context.AppRequest),
	}
}

func (h *HttpService) SID() string {
	return h.sid
}

func (h *HttpService) Reload() (err error) {
	//添加api
	h.addAPI()
	//获取文件描述符
	fds := os.Getenv("httpSocketFd")
	if fds == "" {
		fmt.Printf("reload http service error: no socket fd\n")
		return
	}
	fdi, _ := strconv.Atoi(fds)
	f := os.NewFile(uintptr(3+fdi), "")
	//重用socket
	listener, err := net.FileListener(f)
	if err != nil {
		fmt.Printf("reload http service error: reset listener: %v\n", err.Error())
		return
	}
	h.server = &http.Server{Addr: h.port}
	go h.server.Serve(listener)
	//添加文件描述符
	addFileFD(fdi, f)
	//设定环境变量
	os.Setenv("httpSocketFd", fds)
	return
}

func (h *HttpService) Shutdown() (err error) {
	//6分钟超时
	c, _ := ctx.WithTimeout(ctx.Background(), 360*time.Second)
	h.server.Shutdown(c)
	return
}

func (h *HttpService) Run() (err error) {
	//添加api
	h.addAPI()
	//监听
	h.server = &http.Server{Addr: h.port}
	listener, err := net.Listen("tcp", h.server.Addr)
	go h.server.Serve(listener)
	//添加文件描述符
	tl, _ := listener.(*net.TCPListener)
	f, _ := tl.File()
	fds := fmt.Sprintf("%v", f.Fd())
	fdi, _ := strconv.Atoi(fds)
	addFileFD(fdi, f)
	//设定环境变量
	os.Setenv("httpSocketFd", fds)
	return
}

func (h *HttpService) addAPI() {
	//POST: api-task-add
	http.HandleFunc("/census/task/add", h.apiAddTask)
	//GET: api-task-info
	http.HandleFunc("/census/task/info", h.apiTaskInfo)
}

//获取任务详情
func (h *HttpService) apiTaskInfo(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	var taskUUId = r.FormValue("taskUUId")
	if taskUUId == "" {
		var s, _ = json.Marshal(&context.AppResponse{Code: 1234, Message: "请求数据结构不符合协议"})
		w.Write(s)
		return
	}
	db := mysql.GetInstance(false)
	record := db.Find(records.RecordNameCensusTaskInfo).Select("*").Where("uuid", "=", taskUUId).Execute().Fetch()
	if record == nil {
		var s, _ = json.Marshal(&context.AppResponse{Code: 1240, Message: "任务不存在"})
		w.Write(s)
		return
	}
	taskInfo := record.(*records.CensusTaskInfo)
	respTask := new(model.TaskInfo)
	respTask.UUId = taskInfo.Uuid
	respTask.Status = taskInfo.Status
	respTask.Note = taskInfo.Note
	respTask.Data = taskInfo.Data
	respTask.Explain = taskInfo.Explain
	respTask.StartTime = helper.ParseTime(helper.TimeFormatYmdHis, taskInfo.StartTime)
	respTask.EndTime = helper.ParseTime(helper.TimeFormatYmdHis, taskInfo.EndTime)
	if respTask.StartTime < 0 {
		respTask.StartTime = 0
	}
	if respTask.EndTime < 0 {
		respTask.EndTime = 0
	}
	seconds := respTask.EndTime - respTask.StartTime
	if seconds > 0 {
		respTask.Duration = seconds
	}
	s := h.responseJson(0, taskInfo.Action, taskInfo.AppName, "任务状态", taskInfo.Uuid, respTask)
	w.Write(s)
	logger.Info("http-request-task-info", utils.MakeUUID(), string(s))
	return
}

/*
json
request body post
{
	"appName":"aiclass",
	"action":2001,
	"callback":1, //
	"data":{
		"classId":2007,
		"courseName":"直播课"
	}
}
*/
//添加任务
func (h *HttpService) apiAddTask(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	var body, _ = ioutil.ReadAll(r.Body)
	var rq = new(context.AppRequest)
	err := rq.Decode(body)
	if err != nil {
		var s, _ = json.Marshal(&context.AppResponse{Code: 1234, Message: "请求数据结构不符合协议"})
		w.Write(s)
		return
	}
	if !context.AppExist(rq.AppName) {
		var s, _ = json.Marshal(&context.AppResponse{Code: 1235, Message: "不存在该业务线:" + rq.AppName})
		w.Write(s)
		return
	}
	if context.GetAppTask(rq.AppName, rq.Action) == nil {
		var s, _ = json.Marshal(&context.AppResponse{Code: 1236, Message: "业务线: [" + rq.AppName + "] 不存在 Action: [" + strconv.Itoa(rq.Action) + "]"})
		w.Write(s)
		return
	}
	logger.Info("http-request-task-add", utils.MakeUUID(), "appName:["+rq.AppName+"]", "genTime:", time.Now().Unix(), "raw data", string(body))
	switch rq.CallType {
	case context.CallTypeDirect:
		h.caseCallbackDirect(rq, w)
	case context.CallTypeOver:
		h.caseCallbackOver(rq, w)
	default:
		logger.Warning("http-request-task-add", utils.MakeUUID(), fmt.Sprintf("[call type not exist. error]: appName:(%v) action:(%v)", rq.AppName, rq.Action))
		return
	}
}

//立刻返回
func (h *HttpService) caseCallbackDirect(r *context.AppRequest, w http.ResponseWriter) {
	var t *model.Task
	var s []byte
	var wg = new(sync.WaitGroup)
	wg.Add(1)
	var beforeCallback = func(i interface{}) {
		defer wg.Done()
		if task, ok := i.(*model.Task); ok {
			t = task
		}
	}
	r.AddEvent(context.EventTaskReady, beforeCallback)
	//写入chan
	err := h.Send(r)
	if err != nil {
		wg.Done()
		s = h.responseJson(1238, r.Action, r.AppName, err.Error(), "task error", nil)
		w.Write(s)
		return
	}
	//进行处理
	wg.Wait()
	if t != nil {
		s = h.responseJson(0, r.Action, r.AppName, "请求成功", t.GetTaskUUId(), nil)
	} else {
		s = h.responseJson(1237, r.Action, r.AppName, "请求失败", "task error", nil)
	}
	w.Write(s)
}

//执行结束返回
func (h *HttpService) caseCallbackOver(r *context.AppRequest, w http.ResponseWriter) {
	var t *model.Task
	var s []byte
	//组等待
	wg := new(sync.WaitGroup)
	wg.Add(1)
	var afterCallback = func(i interface{}) {
		defer wg.Done()
		if task, ok := i.(*model.Task); ok {
			t = task
		}
	}
	r.AddEvent(context.EventTaskSuccess, afterCallback)
	r.AddEvent(context.EventTaskFail, afterCallback)
	//写入chan
	err := h.Send(r)
	if err != nil {
		wg.Done()
		s = h.responseJson(1238, r.Action, r.AppName, err.Error(), "task error", nil)
		w.Write(s)
		return
	}
	//进行处理
	wg.Wait()
	if t != nil {
		s = h.responseJson(0, r.Action, r.AppName, "执行结束", t.GetTaskUUId(), t.GetTaskInfo())
	} else {
		s = h.responseJson(1239, r.Action, r.AppName, "执行失败", "task error", nil)
	}
	w.Write(s)
}

func (h *HttpService) responseJson(code, action int, appName, message, taskUUId string, taskInfo interface{}) []byte {
	s, _ := json.Marshal(&context.AppResponse{
		Code:     code,
		AppName:  appName,
		Action:   action,
		TaskUUId: taskUUId,
		Message:  message,
		TaskInfo: taskInfo,
	})
	return s
}
